package org.jeecg.modules.system.controller;

import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.MinioUtil;
import org.jeecg.common.util.filter.FileTypeFilter;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.oss.OssBootUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.Date;

/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @Author scott
 * @since 2018-12-20
 */
@Slf4j
@RestController
@RequestMapping("/sys/common")
public class CommonController {

    @Value(value = "${jeecg.path.upload}")
    private String uploadpath;

    /**
     * 本地：local minio：minio 阿里：alioss
     */
    @Value(value="${jeecg.uploadType}")
    private String uploadType;

    /**  图片上传格式  **/
    public static final String suffixs[] = {".jpg",".gif",".png",".jpeg",".bmp"};

    /**
     * @Author 政辉
     * @return
     */
    @GetMapping("/403")
    public Result<?> noauth()  {
        return Result.error("没有权限，请联系管理员授权");
    }

    /**
     * 文件上传统一方法(舍弃)
     * @param request
     * @param response
     * @return
     */
    @PostMapping(value = "/upload")
    public Result<?> upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Result<?> result = new Result<>();
        String savePath = "";
        String bizPath = request.getParameter("biz");

        //LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞
        if (oConvertUtils.isNotEmpty(bizPath)) {
            if(bizPath.contains(SymbolConstant.SPOT_SINGLE_SLASH) || bizPath.contains(SymbolConstant.SPOT_DOUBLE_BACKSLASH)){
                throw new JeecgBootException("上传目录bizPath，格式非法！");
            }
        }

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        // 获取上传文件对象
        MultipartFile file = multipartRequest.getFile("file");
        if(oConvertUtils.isEmpty(bizPath)){
            if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){
                //未指定目录，则用阿里云默认目录 upload
                bizPath = "upload";
                //result.setMessage("使用阿里云文件上传时，必须添加目录！");
                //result.setSuccess(false);
                //return result;
            }else{
                bizPath = "";
            }
        }
        if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
            //update-begin-author:liusq date:20221102 for: 过滤上传文件类型
            FileTypeFilter.fileTypeFilter(file);
            //update-end-author:liusq date:20221102 for: 过滤上传文件类型
            //update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传
            savePath = this.uploadLocal(file,bizPath,2);
            //update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传
            /**  富文本编辑器及markdown本地上传时，采用返回链接方式
            //针对jeditor编辑器如何使 lcaol模式，采用 base64格式存储
            String jeditor = request.getParameter("jeditor");
            if(oConvertUtils.isNotEmpty(jeditor)){
                result.setMessage(CommonConstant.UPLOAD_TYPE_LOCAL);
                result.setSuccess(true);
                return result;
            }else{
                savePath = this.uploadLocal(file,bizPath);
            }
            */
        }else{
            //update-begin-author:taoyan date:20200814 for:文件上传改造
            savePath = CommonUtils.upload(file, bizPath, uploadType);
            //update-end-author:taoyan date:20200814 for:文件上传改造
        }
        if(oConvertUtils.isNotEmpty(savePath)){
            result.setMessage(savePath);
            result.setSuccess(true);
        }else {
            result.setMessage("上传失败！");
            result.setSuccess(false);
        }
        return result;
    }

    /**
     * 图片上传统一方法
     * @param request
     * @param response
     * @return
     */
    @PostMapping(value = "/uploadImg")
    public Result<?> uploadImg(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String savePath = "";//数据库保存路径
        String bizPath = request.getParameter("biz");
        if(oConvertUtils.isEmpty(bizPath)){
            bizPath = "temp";
        }

        //图片文件夹标记
        bizPath = "uploadImg/" + bizPath;
        //针对jeditor编辑器如何使 lcaol模式，采用 base64格式存储
        String jeditor = request.getParameter("jeditor");
        if(oConvertUtils.isNotEmpty(jeditor) && "1".equals(jeditor)){
            bizPath += "/jeditor";
        }
        System.out.println("图片保存地址：" + bizPath);

        //获取请求过来的文件对象
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象

        //获取原文件名
        String orgName = file.getOriginalFilename();
        if(oConvertUtils.isEmpty(orgName)){
            return Result.error("文件对象不能为空！");
        }
        //运算转换单位（保留2位小数）--- 转换单位为MB
        double fileSizeMB = new BigDecimal(file.getSize()/1024/1024).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        if (fileSizeMB > 10) {
            return Result.error("文件不能超过10M！");
        }
        // 文件名后缀处理---start---  .jsp
        String suffix_z = orgName.substring(orgName.lastIndexOf("."), orgName.length());
        if(!useLoop(suffixs, suffix_z)){
            return Result.error("文件格式错误！");
        }

        //根据配置的文件上传类型选择文件上传
        if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
            savePath = this.uploadLocal(file,bizPath,1);//本地文件上传
        }else if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
            savePath = MinioUtil.upload(file,bizPath,null);//minio文件上传
        }else if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){
            savePath = OssBootUtil.upload(file,bizPath);//阿里云OSS文件上传
        }

        if(oConvertUtils.isEmpty(savePath)){
            return Result.error("上传失败！");
        }

        return Result.ok("上传成功！",savePath);
    }

    /**
     * 文件上传统一方法
     * @param request
     * @param response
     * @return
     */
    @PostMapping(value = "/uploadFile")
    public Result<?> uploadFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String savePath = "";//数据库保存路径
        String bizPath = request.getParameter("biz");
        if(oConvertUtils.isEmpty(bizPath)){
            bizPath = "unified";
        }
        //文件的文件夹标记
        bizPath = "uploadFile/" + bizPath;

        //获取请求过来的文件对象
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象

        //获取原文件名
        String orgName = file.getOriginalFilename();
        if(oConvertUtils.isEmpty(orgName)){
            return Result.error("文件对象不能为空！");
        }

        //根据配置的文件上传类型选择文件上传
        if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
            savePath = this.uploadLocal(file,bizPath,2);//本地文件上传
        }else if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
            savePath = MinioUtil.upload(file,bizPath,null);//minio文件上传
        }else if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){
            savePath = OssBootUtil.upload(file,bizPath);//阿里云OSS文件上传
        }

        if(oConvertUtils.isEmpty(savePath)){
            return Result.error("上传失败！");
        }

        return Result.ok("上传成功！",savePath);
    }

    /**
     * 本地文件上传
     * @param multipartFile 文件
     * @param bizPath  自定义文件夹
     * @param type  1：图片上传，2：文件上传
     * @return
     */
    public String uploadLocal(MultipartFile multipartFile,String bizPath,int type){
        try {
            String ctxPath = uploadpath;//文件上传根目录
            String fileName = null;//文件新名称
            File file = new File(ctxPath + File.separator + bizPath + File.separator );
            if (!file.exists()) {
                file.mkdirs();// 创建文件根目录
            }

            // 获取文件名
            String orgName = multipartFile.getOriginalFilename();
            if(type == 1){
                // 文件名后缀处理---start---  .jsp
                String suffix_z = orgName.substring(orgName.lastIndexOf("."), orgName.length());
                //文件新名称
                fileName = DateUtils.sdf7.format(new Date()) + "_" + (int)(Math.random()*900000+100000) + suffix_z;
            }else{
                fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.indexOf("."));
            }

            String savePath = file.getPath() + File.separator + fileName;
            File savefile = new File(savePath);
            FileCopyUtils.copy(multipartFile.getBytes(), savefile);
            String dbpath = File.separator + bizPath + File.separator + fileName;
            if (dbpath.contains("\\")) {
                dbpath = dbpath.replace("\\", "/");
            }
            return dbpath;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return "";
    }

    /**
     * 预览图片&下载文件
     * 请求地址：http://localhost:8080/common/static/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
     *
     * @param request
     * @param response
     */
    @GetMapping(value = "/static/**")
    public void view(HttpServletRequest request, HttpServletResponse response) {
        // ISO-8859-1 ==> UTF-8 进行编码转换
        String imgPath = extractPathFromPattern(request);
        System.out.println("============imgPath===============" + imgPath);
        if(oConvertUtils.isEmpty(imgPath) || CommonConstant.STRING_NULL.equals(imgPath)){
            return;
        }
        // 其余处理略
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            imgPath = imgPath.replace("..", "").replace("../","");
            if (imgPath.endsWith(SymbolConstant.COMMA)) {
                imgPath = imgPath.substring(0, imgPath.length() - 1);
            }
            String filePath = uploadpath + File.separator + imgPath;
            File file = new File(filePath);
            if(!file.exists()){
                response.setStatus(404);
                throw new RuntimeException("文件["+imgPath+"]不存在..");
            }
            // 设置强制下载不打开
            response.setContentType("application/force-download");
            response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1"));
            inputStream = new BufferedInputStream(new FileInputStream(filePath));
            outputStream = response.getOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, len);
            }
            response.flushBuffer();
        } catch (IOException e) {
            log.error("预览文件失败" + e.getMessage());
            response.setStatus(404);
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            }
        }

    }

    /**
     * 文件下载
     * @param downUrl
     * @param request
     * @param response
     */
    @GetMapping("/downloadFile")
    public void downloadFiles(HttpServletRequest request, HttpServletResponse response) {
        String downUrl = request.getParameter("file");
        System.out.println("============downUrl===============" + downUrl);
        OutputStream outputStream = null;
        InputStream inputStream = null;
        BufferedInputStream bufferedInputStream = null;
        byte[] bytes = new byte[1024];
        File file = new File(downUrl);
        String fileName = file.getName();
        // 获取输出流
        try {
            // StandardCharsets.ISO_8859_1 *=UTF-8'
            // response.setHeader("Content-Disposition", "attachment;filename=" +  new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            // 以流的形式返回文件
            response.setContentType("application/octet-stream;charset=utf-8");
            inputStream = new FileInputStream(file);
            bufferedInputStream = new BufferedInputStream(inputStream);
            outputStream = response.getOutputStream();
            int i = bufferedInputStream.read(bytes);
            while (i != -1) {
                outputStream.write(bytes, 0, i);
                i = bufferedInputStream.read(bytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
                if (bufferedInputStream != null) {
                    bufferedInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * @功能：pdf预览Iframe
     * @param modelAndView
     * @return
     */
    @RequestMapping("/pdf/pdfPreviewIframe")
    public ModelAndView pdfPreviewIframe(ModelAndView modelAndView) {
        modelAndView.setViewName("pdfPreviewIframe");
        return modelAndView;
    }

    /**
     *  把指定URL后的字符串全部截断当成参数
     *  这么做是为了防止URL中包含中文或者特殊字符（/等）时，匹配不了的问题
     * @param request
     * @return
     */
    private static String extractPathFromPattern(final HttpServletRequest request) {
        String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
    }

    //使用简单的循环语句判断数组中是否包含某个字符
    public static boolean useLoop(String[] arr, String targetValue) {
        for (String s : arr) {
            if (s.equals(targetValue)) {
                return true;
            }
        }
        return false;
    }

}
